
package com.jack.helloworld.service;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.Service;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.provider.Settings;
import android.util.Log;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.SortedMap;
import java.util.TimerTask;
import java.util.TreeMap;

public class AppMonitorService extends Service {

    private ActivityManager mActivityManager;
    private HandlerThread handlerThread = null;
    private Handler bottomFloatHandler;
    private TimerTask bottomFloatTask;
    private int currentType = -1;
    private final static int LOOPHANDLER = 11110;
    private Handler mHandler = null;
    private boolean hasShowFlag = false;
    private boolean welcomeFirstShow = false;
    private Context mContext;
    public static final String CURRENTNULL = "CurrentNULL";
    public static final String TAG = "AppService";
    private OnTopAppChangeListener mOnTopAppChangeListener;

    private String prePackageName = "";
    private String curPackageName = "";

    /** first app user */
    public static final int AID_APP = 10000;
    /** offset for uid ranges for each user */
    public static final int AID_USER = 100000;

    public class AppMonitorBinderService extends Binder {

        public AppMonitorService getService() {
            return AppMonitorService.this;
        }
    }

    public void setOnTopAppChangeListener (OnTopAppChangeListener onTopAppChangeListener) {
        this.mOnTopAppChangeListener = onTopAppChangeListener;
    }

    public interface OnTopAppChangeListener {
        void onTopAppChange(String packageName);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new AppMonitorBinderService();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY; // 用于保证service被kill后重新创建
    }

    @Override
    public void onCreate() {
        super.onCreate();
        /**
         * 当service运行在低内存的环境时，将会kill掉一些存在的进程。因此进程的优先级将会很重要， 可以使用startForeground
         * API将service放到前台状态。这样在低内存时被kill的几率更低
         */

        mActivityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);

        startForeground(0, new Notification());
        handlerThread = new HandlerThread("count_thread");
        handlerThread.start();

        bottomFloatHandler = new Handler() {
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d(TAG, "msg.what:" + msg.what);
            }
        };

        mHandler = new Handler(handlerThread.getLooper()) {
            public void dispatchMessage(Message msg) {
                switch (msg.what) {
                    case LOOPHANDLER:

                        String packageName;
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                            packageName = getTaskPackname();// ApkUtil.getActivePackages(mActivityManager);
                            if (packageName == null || packageName.isEmpty()
                                    || packageName.equals(CURRENTNULL))
                            {
                                Log.d(TAG, "SDK_INT >= 21, packageName:" + packageName);

                                //useful on android5.1
                                packageName = getForegroundApp();
//                                packageName = AndroidProcesses.getRunningAppProcesses().get(0).getPackageName();
                                Log.e(TAG, "packageName:" + packageName);

                            }
                        } else {
                            packageName = getTopApp();
                            if (packageName == null || packageName.isEmpty()
                                    || packageName.equals(CURRENTNULL))
                            {
                                packageName = getActivePackagesCompat(mActivityManager);
                                Log.d(TAG, "SDK_INT < 21, packageName:" + packageName);
                            }
                        }
                        if (packageName == null || packageName.isEmpty()
                                || packageName.equals(CURRENTNULL)) {
                            Log.d(TAG, "无法获取到当前栈顶包名");
                            break;
                        }

                        if (!curPackageName.equals(packageName)) {
                            prePackageName = curPackageName;
                            curPackageName = packageName;

                            if (mOnTopAppChangeListener != null) {
                                mOnTopAppChangeListener.onTopAppChange(packageName);
                            }
                        }

                        break;

                }
                mHandler.sendEmptyMessageDelayed(LOOPHANDLER, 500);
            }

        };
        mHandler.sendEmptyMessage(LOOPHANDLER);

    }

    private boolean tryToGetPermissions() {
        AppOpsManager mAppOpsManager = (AppOpsManager) getApplicationContext().getSystemService(
                Context.APP_OPS_SERVICE);
        Log.d(TAG, "appops services:" + mAppOpsManager);
        Class c = mAppOpsManager.getClass();
        PackageInfo packageInfo = null;
        try {
            packageInfo = getApplicationContext().getPackageManager().getPackageInfo(
                    getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
        } catch (NameNotFoundException e1) {
            e1.printStackTrace();
        }

        try {
            Class[] cArg = new Class[4];
            cArg[0] = int.class;
            cArg[1] = int.class;
            cArg[2] = String.class;
            cArg[3] = int.class;
            Method lMethod = c.getDeclaredMethod("setMode", cArg);
            // OP_GET_USAGE_STATS = 43;
            return (Boolean) lMethod.invoke(packageInfo, 43, packageInfo.applicationInfo.uid,
                    getApplicationContext().getPackageName(), AppOpsManager.MODE_ALLOWED);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return false;
    }

    private boolean tryToGetAppops() {
        if (Build.VERSION.SDK_INT >= 19) {
            Object object = getApplicationContext().getSystemService(Context.APP_OPS_SERVICE);
            Log.d(TAG, "appops services:" + object);
            Class c = object.getClass();
            try {
                Class[] cArg = new Class[4];
                cArg[0] = int.class;
                cArg[1] = int.class;
                cArg[2] = String.class;
                cArg[3] = int.class;
                Method lMethod = c.getDeclaredMethod("setMode", cArg);
                // OP_GET_USAGE_STATS = 43;
                return (Boolean) lMethod.invoke(object, 43, Binder.getCallingUid(),
                        getApplicationContext().getPackageName(), AppOpsManager.MODE_ALLOWED);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * UsageStatsManager类是在AndroidL以上才有的，所以导入sdk版本要在Android5.0以上 使用此方法需要在设备中的“有权查看使用情况的应用”模块中开启应用权限
     *
     * @return
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @SuppressLint("NewApi")
    private String getTaskPacknameByUsageStats() {
        String currentApp = CURRENTNULL;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            UsageStatsManager usm = (UsageStatsManager) this
                    .getSystemService(Context.USAGE_STATS_SERVICE);
            long time = System.currentTimeMillis();
            List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
                    time - 1000 * 1000, time);
            if (appList != null && appList.size() > 0) {
                SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
                for (UsageStats usageStats : appList) {
                    mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
                }
                if (mySortedMap != null && !mySortedMap.isEmpty()) {
                    currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
                }
            }
        } else {
            ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
            List<RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
            currentApp = tasks.get(0).processName;
        }
        Log.d(TAG, "getTaskPacknameByUsageStats -> Current App in foreground is: " + currentApp);
        return currentApp;
    }

    /**
     * 获取程序包名(本程序包名5.0版本上下都可获取)
     * 注：该方法在获取小米Android5.0.2版本的手机栈顶包名时是获取不到的（魅族、三星、华为、酷派、vivo都可以获取到），可以使用下面的方法二来实现
     *
     * @return
     */
    private String getTaskPackname() {
        RunningAppProcessInfo currentInfo = null;
        Field field = null;
        int START_TASK_TO_FRONT = 2;
        String currentApp = CURRENTNULL;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            try {
                field = RunningAppProcessInfo.class
                        .getDeclaredField("processState");
            } catch (Exception e) {
                e.printStackTrace();
            }
            ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
            List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
            for (RunningAppProcessInfo app : appList) {
                if (app.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                    Integer state = null;
                    try {
                        state = field.getInt(app);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (state != null && state == START_TASK_TO_FRONT) {
                        currentInfo = app;
                        break;
                    }
                }
            }
            if (currentInfo != null) {
                currentApp = currentInfo.processName;
            }
        } else {
            ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
            List<RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
            currentApp = tasks.get(0).processName;
        }
        Log.d(TAG, "getTaskPackname: " + currentApp);
        return currentApp;
    }

    // 5.0之后无法使用
    private String getTopApp() {
        String currentApp = CURRENTNULL;
        ActivityManager mActivityManager;
        mActivityManager = (ActivityManager) AppMonitorService.this
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningTaskInfo> RunningTask = mActivityManager.getRunningTasks(1);
        if (RunningTask == null || RunningTask.size() == 0) {
            return currentApp;
        }
        RunningTaskInfo taskInfo = RunningTask.get(0);
        currentApp = taskInfo.topActivity.getPackageName().toString();
        Log.d(TAG, "getTopApp: " + currentApp);
        return currentApp;
    }

    // sdk <21 得到最顶层的Activity名字
    public static String getActivePackagesCompat(ActivityManager am) {
        if (am == null)
            return "";
        List<RunningTaskInfo> tasklist = am.getRunningTasks(1);
        ComponentName topActivity = tasklist.get(0).topActivity;
        String packageName = topActivity.getPackageName();
        return packageName;
    }

    private boolean hasOption() {
        PackageManager packageManager = getApplicationContext()
                .getPackageManager();
        Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
        List<ResolveInfo> list = packageManager.queryIntentActivities(intent,
                PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : list) {
            Log.d(TAG, "resolveInfo:" + resolveInfo.toString());
        }
        return list.size() > 0;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private boolean hasSwitched() {
        long ts = System.currentTimeMillis();
        UsageStatsManager usageStatsManager = (UsageStatsManager) getApplicationContext()
                .getSystemService(Context.USAGE_STATS_SERVICE);
        List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats(
                UsageStatsManager.INTERVAL_BEST, 0, ts);
        if (queryUsageStats == null || queryUsageStats.isEmpty()) {
            return false;
        }
        return true;
    }

    private void goToUsageSettings() {
        Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }

    @Override
    public void onDestroy() {
        stopForeground(true);
        Intent localIntent = new Intent();
        localIntent.setClass(this, AppMonitorService.class);
        this.startService(localIntent); // 销毁时重新启动Service
    }

    public static String getForegroundApp() {
        File[] files = new File("/proc").listFiles();
        int lowestOomScore = Integer.MAX_VALUE;
        String foregroundProcess = null;
        for (File file : files) {
            if (!file.isDirectory()) {
                continue;
            }
            int pid;
            try {
                pid = Integer.parseInt(file.getName());
            } catch (NumberFormatException e) {
                continue;
            }
            try {
                String cgroup = read(String.format("/proc/%d/cgroup", pid));
                String[] lines = cgroup.split("\n");
                String cpuSubsystem;
                String cpuaccctSubsystem;

                if (lines.length == 2) {// 有的手机里cgroup包含2行或者3行，我们取cpu和cpuacct两行数据
                    cpuSubsystem = lines[0];
                    cpuaccctSubsystem = lines[1];
                } else if (lines.length == 3) {
                    cpuSubsystem = lines[0];
                    cpuaccctSubsystem = lines[2];
                } else {
                    continue;
                }
                if (!cpuaccctSubsystem.endsWith(Integer.toString(pid))) {
                    // not an application process
                    continue;
                }
                if (cpuSubsystem.endsWith("bg_non_interactive")) {
                    // background policy
                    continue;
                }
                String cmdline = read(String.format("/proc/%d/cmdline", pid));
                if (cmdline.contains("com.android.systemui")) {
                    continue;
                }
                int uid = Integer.parseInt(
                        cpuaccctSubsystem.split(":")[2].split("/")[1].replace("uid_", ""));
                if (uid >= 1000 && uid <= 1038) {
                    // system process
                    continue;
                }
                int appId = uid - AID_APP;
                int userId = 0;
                // loop until we get the correct user id.
                // 100000 is the offset for each user.
                while (appId > AID_USER) {
                    appId -= AID_USER;
                    userId++;
                }
                if (appId < 0) {
                    continue;
                }
                // u{user_id}_a{app_id} is used on API 17+ for multiple user account support.
                 String uidName = String.format("u%d_a%d", userId, appId);
                 Log.d(TAG, "cmdline:" + cmdline + "  , uidName:" + uidName);
                File oomScoreAdj = new File(String.format("/proc/%d/oom_score_adj", pid));
                if (oomScoreAdj.canRead()) {
                    int oomAdj = Integer.parseInt(read(oomScoreAdj.getAbsolutePath()));
                    if (oomAdj != 0) {
                        continue;
                    }
                }
                int oomscore = Integer.parseInt(read(String.format("/proc/%d/oom_score", pid)));
                if (oomscore < lowestOomScore) {
                    lowestOomScore = oomscore;
                    foregroundProcess = cmdline;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return foregroundProcess;
    }

    private static String read(String path) throws IOException {
        StringBuilder output = new StringBuilder();
        BufferedReader reader = new BufferedReader(new FileReader(path));
        output.append(reader.readLine());
        for (String line = reader.readLine(); line != null; line = reader.readLine()) {
            output.append('\n').append(line);
        }
        reader.close();
        return output.toString().trim();// 不调用trim()，包名后面会带有乱码
    }

}
